package de.hauschild.jchronology;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Map.Entry;

import javax.management.timer.Timer;
import javax.swing.JButton;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.text.MaskFormatter;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.StreamException;
import com.toedter.calendar.JDateChooser;

import de.hauschild.jchronology.data1.Chronology;
import de.hauschild.jchronology.data1.ChronologyEntry;

public class JChronologyFrame extends JFrame {

  private final static XStream XSTREAM = new XStream();
  private static final DateFormat DATE_FORMAT = new SimpleDateFormat("dd.MM.yyyy");

  private static MaskFormatter dateFormatter;

  static {
    try {
      dateFormatter = new MaskFormatter("##:##");
    } catch (final ParseException ex) {
      System.exit(1);
    }
  }

  private static final long serialVersionUID = 6924049703547816587L;

  private final File chronologyFile;
  private JDateChooser dateChooser;
  private Chronology chronology;
  private JFormattedTextField startTimeTextField;
  private JFormattedTextField endTimeTextField;
  private JFormattedTextField breakDurationTextField;
  private JFormattedTextField targetDurationTextField;
  private JFormattedTextField resultDurationTextField;
  private JLabel weekOfYearLabel;
  private JLabel mondayDateLabel;
  private JLabel tuesdayDateLabel;
  private JLabel wednesdayDateLabel;
  private JLabel thursdayDateLabel;
  private JLabel fridayDateLabel;
  private JTextField mondayDurationTextField;
  private JTextField tuesdayDurationTextField;
  private JTextField wednesdayDurationTextField;
  private JTextField thursdayDurationTextField;
  private JTextField fridayDurationTextField;
  private JTextField weekDurationTextField;
  private JLabel firstDateLabel;
  private JTextField overallResultDurationTextField;
  private JTextField weekTimeTextField;

  public JChronologyFrame(final File theChronologyFile) {
    chronologyFile = theChronologyFile;
    setTitle("JChronology - " + FilenameUtils.getName(chronologyFile.getAbsolutePath()));
    setLayout(new BorderLayout());
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    setVisible(true);
    buildUI();
    initData();
    updateData();
    pack();
    setLocationRelativeTo(null);
  }

  private JPanel buildDayOverviewPanel() {
    final JPanel dayOverviewPanelPanel = new JPanel(new GridLayout(0, 3));
    dayOverviewPanelPanel.add(new JLabel("Arbeitsbeginn:"));
    startTimeTextField = new JFormattedTextField(dateFormatter);
    dayOverviewPanelPanel.add(startTimeTextField);
    final JButton startTimeNowButton = new JButton("jetzt");
    startTimeNowButton.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(final ActionEvent theE) {
        startTimeTextField.setText(DateUtils.format(new Date()));
        writeData();
        updateData();
      }
    });
    dayOverviewPanelPanel.add(startTimeNowButton);
    dayOverviewPanelPanel.add(new JLabel("Arbeitsende:"));
    endTimeTextField = new JFormattedTextField(dateFormatter);
    dayOverviewPanelPanel.add(endTimeTextField);
    final JButton endTimeButton = new JButton("jetzt");
    endTimeButton.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(final ActionEvent theE) {
        endTimeTextField.setText(DateUtils.format(new Date()));
        writeData();
        updateData();
      }
    });
    dayOverviewPanelPanel.add(endTimeButton);
    dayOverviewPanelPanel.add(new JLabel("Pause:"));
    breakDurationTextField = new JFormattedTextField(dateFormatter);
    dayOverviewPanelPanel.add(breakDurationTextField);
    dayOverviewPanelPanel.add(new JLabel());
    dayOverviewPanelPanel.add(new JLabel("Sollzeit:"));
    targetDurationTextField = new JFormattedTextField(dateFormatter);
    dayOverviewPanelPanel.add(targetDurationTextField);
    dayOverviewPanelPanel.add(new JLabel());
    final JLabel resultDurationLabel = new JLabel("Istzeit:");
    resultDurationLabel.setForeground(Color.BLUE);
    dayOverviewPanelPanel.add(resultDurationLabel);
    resultDurationTextField = new JFormattedTextField();
    resultDurationTextField.setEditable(false);
    resultDurationTextField.setForeground(Color.BLUE);
    dayOverviewPanelPanel.add(resultDurationTextField);
    final JButton saveButton = new JButton("Speichern");
    saveButton.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(final ActionEvent theE) {
        writeData();
        updateData();
      }
    });
    dayOverviewPanelPanel.add(saveButton);
    return dayOverviewPanelPanel;
  }

  private JPanel buildOverallPanel() {
    final JPanel overallPanel = new JPanel(new GridLayout(0, 2));
    overallPanel.add(new JLabel("erste Eintrag:"));
    firstDateLabel = new JLabel();
    overallPanel.add(firstDateLabel);
    final JLabel overallResultDurationLabel = new JLabel("Billanz:");
    overallResultDurationLabel.setForeground(Color.BLUE);
    overallPanel.add(overallResultDurationLabel);
    overallResultDurationTextField = new JTextField();
    overallResultDurationTextField.setForeground(Color.BLUE);
    overallPanel.add(overallResultDurationTextField);
    return overallPanel;
  }

  private void buildUI() {
    dateChooser = new JDateChooser();
    dateChooser.addPropertyChangeListener("date", new PropertyChangeListener() {
      @Override
      public void propertyChange(final PropertyChangeEvent theEvt) {
        updateData();
      }
    });
    add(dateChooser, BorderLayout.NORTH);
    final JTabbedPane tabbedPane = new JTabbedPane();
    tabbedPane.addTab("Tag", buildDayOverviewPanel());
    tabbedPane.addTab("Woche", buildWeekOverviewPanel());
    tabbedPane.addTab("Geamt", buildOverallPanel());
    add(tabbedPane, BorderLayout.CENTER);
  }

  private JPanel buildWeekOverviewPanel() {
    final JPanel weekOverviewPanel = new JPanel(new GridLayout(0, 3));
    mondayDateLabel = new JLabel("");
    weekOverviewPanel.add(mondayDateLabel);
    weekOverviewPanel.add(new JLabel("Montag:"));
    mondayDurationTextField = new JTextField();
    mondayDurationTextField.setEditable(false);
    weekOverviewPanel.add(mondayDurationTextField);
    tuesdayDateLabel = new JLabel("");
    weekOverviewPanel.add(tuesdayDateLabel);
    weekOverviewPanel.add(new JLabel("Dienstag:"));
    tuesdayDurationTextField = new JTextField("");
    tuesdayDurationTextField.setEditable(false);
    weekOverviewPanel.add(tuesdayDurationTextField);
    wednesdayDateLabel = new JLabel("");
    weekOverviewPanel.add(wednesdayDateLabel);
    weekOverviewPanel.add(new JLabel("Mittwoch:"));
    wednesdayDurationTextField = new JTextField("");
    wednesdayDurationTextField.setEditable(false);
    weekOverviewPanel.add(wednesdayDurationTextField);
    thursdayDateLabel = new JLabel("");
    weekOverviewPanel.add(thursdayDateLabel);
    weekOverviewPanel.add(new JLabel("Donnerstag:"));
    thursdayDurationTextField = new JTextField("");
    thursdayDurationTextField.setEditable(false);
    weekOverviewPanel.add(thursdayDurationTextField);
    fridayDateLabel = new JLabel("");
    weekOverviewPanel.add(fridayDateLabel);
    weekOverviewPanel.add(new JLabel("Freitag:"));
    fridayDurationTextField = new JTextField("");
    fridayDurationTextField.setEditable(false);
    weekOverviewPanel.add(fridayDurationTextField);
    weekOfYearLabel = new JLabel("");
    weekOfYearLabel.setForeground(Color.BLUE);
    weekOverviewPanel.add(weekOfYearLabel);
    final JLabel weekTimeLabel = new JLabel("Istzeit");
    weekTimeLabel.setForeground(Color.BLUE);
    weekOverviewPanel.add(weekTimeLabel);
    weekTimeTextField = new JTextField();
    weekTimeTextField.setEditable(false);
    weekOverviewPanel.add(weekTimeTextField);
    weekOverviewPanel.add(new JLabel());
    final JLabel weekLabel = new JLabel("Gesamt:");
    weekLabel.setForeground(Color.BLUE);
    weekOverviewPanel.add(weekLabel);
    weekDurationTextField = new JTextField("");
    weekDurationTextField.setEditable(false);
    weekDurationTextField.setForeground(Color.BLUE);
    weekOverviewPanel.add(weekDurationTextField);
    return weekOverviewPanel;
  }

  private Duration getBreakDuration() {
    return getDuration(breakDurationTextField);
  }

  private Date getCurrentDate() {
    return DateUtils.truncate(dateChooser.getDate());
  }

  private ChronologyEntry getCurrentEntry() {
    final Date currentDate = dateChooser.getDate();
    final ChronologyEntry currentEntry = chronology.getEntry(currentDate);
    return currentEntry;
  }

  private Date getDate(final JTextField theTextField) {
    final Duration duration = new Duration(theTextField.getText());
    final Date currentDate = getCurrentDate();
    return new Date(currentDate.getTime() + duration.getDuration());
  }

  private Duration getDefaultBreakDuration() {
    final ChronologyEntry entryBeforeCurrent = getEntryBeforeCurrent();
    if (entryBeforeCurrent != null) {
      return entryBeforeCurrent.getBreakDuration();
    }
    return new Duration(Timer.ONE_MINUTE * 45);
  }

  private Duration getDefaultTargetDuration() {
    final ChronologyEntry entryBeforeCurrent = getEntryBeforeCurrent();
    if (entryBeforeCurrent != null) {
      return entryBeforeCurrent.getTargetDuration();
    }
    return new Duration(Timer.ONE_HOUR * 8);
  }

  private Duration getDuration(final JTextField theTextField) {
    return new Duration(theTextField.getText());
  }

  private Date getEndTime() {
    return getDate(endTimeTextField);
  }

  private ChronologyEntry getEntryBeforeCurrent() {
    final Date currentDate = getCurrentDate();
    for (int day = 1; day < 5; day++) {
      final Date dateBeforeCurrent = new Date(currentDate.getTime() - day * Timer.ONE_DAY);
      final ChronologyEntry entry = chronology.getEntry(dateBeforeCurrent);
      if (!entry.isEmpty()) {
        return entry;
      }
    }
    return null;
  }

  private Date getStartTime() {
    return getDate(startTimeTextField);
  }

  private Duration getTargetDuration() {
    return getDuration(targetDurationTextField);
  }

  private void initData() {
    try {
      chronology = (Chronology) XSTREAM.fromXML(new FileInputStream(chronologyFile));
    } catch (final FileNotFoundException ex) {
      JOptionPane.showMessageDialog(this, "Datei kann nicht gelesen werden.");
      System.exit(1);
    } catch (final StreamException ex) {
      chronology = new Chronology();
      writeData();
    }
  }

  private void setDateLabelText(final JLabel theLabel, final Date theDate) {
    theLabel.setText(DATE_FORMAT.format(theDate));
  }

  private void setDurationTextFieldText(final JTextField theTetxField, final Duration theDuration) {
    if (theDuration != null) {
      theTetxField.setText(theDuration.toString());
      if (theDuration.getDuration() < 0) {
        theTetxField.setBackground(Color.RED);
      } else {
        theTetxField.setBackground(Color.GREEN);
      }
    } else {
      theTetxField.setText(StringUtils.EMPTY);
      theTetxField.setBackground(Color.WHITE);
    }
  }

  private void updateData() {
    updateDayOverviewPanel();
    updateWeekOverviewPanel();
    updateOverallPanel();
  }

  private void updateDayOverviewPanel() {
    final ChronologyEntry currentEntry = getCurrentEntry();
    final Date startTime = currentEntry.getStartTime();
    if (startTime != null) {
      startTimeTextField.setText(DateUtils.format(startTime));
    } else {
      startTimeTextField.setText(StringUtils.EMPTY);
    }
    final Date endTime = currentEntry.getEndTime();
    if (endTime != null) {
      endTimeTextField.setText(DateUtils.format(endTime));
    } else {
      endTimeTextField.setText(StringUtils.EMPTY);
    }
    Duration breakDuration = currentEntry.getBreakDuration();
    if (breakDuration == null) {
      breakDuration = getDefaultBreakDuration();
    }
    breakDurationTextField.setText(breakDuration.toString());
    Duration targetDuration = currentEntry.getTargetDuration();
    if (targetDuration == null) {
      targetDuration = getDefaultTargetDuration();
    }
    targetDurationTextField.setText(targetDuration.toString());
    final Duration resultDuration = currentEntry.getResultDuration();
    setDurationTextFieldText(resultDurationTextField, resultDuration);
  }

  private void updateOverallPanel() {
    Date firstEntryDate = null;
    final Duration overallDuration = new Duration();
    for (final Entry<Date, ChronologyEntry> entry : chronology.entrySet()) {
      if (firstEntryDate == null) {
        firstEntryDate = entry.getKey();
      } else {
        if (firstEntryDate.after(entry.getKey())) {
          firstEntryDate = entry.getKey();
        }
      }
      overallDuration.add(entry.getValue().getResultDuration());
    }
    setDateLabelText(firstDateLabel, firstEntryDate);
    setDurationTextFieldText(overallResultDurationTextField, overallDuration);
  }

  private void updateWeekOverviewPanel() {
    weekOfYearLabel.setText(String.format("%02d. KW", CalendarUtils.get(getCurrentDate(), Calendar.WEEK_OF_YEAR)));
    int dayOfWeek = CalendarUtils.get(getCurrentDate(), Calendar.DAY_OF_WEEK);
    if (dayOfWeek == Calendar.SUNDAY) {
      dayOfWeek = Calendar.SATURDAY + 1;
    }
    final Duration weekDuration = new Duration();
    final Duration weekTime = new Duration();
    boolean weekDurationChanged = false;
    for (int day = Calendar.MONDAY; day <= Calendar.FRIDAY; day++) {
      final int dayDelta = dayOfWeek - day;
      final Date date = new Date(getCurrentDate().getTime() - dayDelta * Timer.ONE_DAY);
      final ChronologyEntry entry = chronology.getEntry(date);
      if (entry.getResultDuration() != null) {
        weekDurationChanged = true;
      }
      weekDuration.add(entry.getResultDuration());
      weekTime.add(entry.getDuration());
      switch (day) {
      case Calendar.MONDAY:
        setDateLabelText(mondayDateLabel, date);
        setDurationTextFieldText(mondayDurationTextField, entry.getResultDuration());
        break;
      case Calendar.TUESDAY:
        setDateLabelText(tuesdayDateLabel, date);
        setDurationTextFieldText(tuesdayDurationTextField, entry.getResultDuration());
        break;
      case Calendar.WEDNESDAY:
        setDateLabelText(wednesdayDateLabel, date);
        setDurationTextFieldText(wednesdayDurationTextField, entry.getResultDuration());
        break;
      case Calendar.THURSDAY:
        setDateLabelText(thursdayDateLabel, date);
        setDurationTextFieldText(thursdayDurationTextField, entry.getResultDuration());
        break;
      case Calendar.FRIDAY:
        setDateLabelText(fridayDateLabel, date);
        setDurationTextFieldText(fridayDurationTextField, entry.getResultDuration());
        break;
      }
    }
    if (weekDurationChanged) {
      setDurationTextFieldText(weekDurationTextField, weekDuration);
      setDurationTextFieldText(weekTimeTextField, weekTime);
    } else {
      setDurationTextFieldText(weekDurationTextField, null);
      setDurationTextFieldText(weekTimeTextField, null);
    }
  }

  private void writeData() {
    final ChronologyEntry currentEntry = getCurrentEntry();
    try {
      currentEntry.setStartTime(getStartTime());
    } catch (final IllegalArgumentException ex) {
    }
    try {
      currentEntry.setEndTime(getEndTime());
    } catch (final IllegalArgumentException ex) {
    }
    try {
      currentEntry.setBreakDuration(getBreakDuration());
    } catch (final IllegalArgumentException ex) {
    }
    try {
      currentEntry.setTargetDuration(getTargetDuration());
    } catch (final IllegalArgumentException ex) {
    }
    try {
      chronology.clean();
      XSTREAM.toXML(chronology, new FileOutputStream(chronologyFile));
    } catch (final FileNotFoundException ex) {
      JOptionPane.showMessageDialog(this, "Kann Datei nicht schreiben.");
      System.exit(1);
    }
  }
}
